package com.errol.batchprocessor.employeeaddition.advisorimpl.enhance.preload;

import com.baomidou.mybatisplus.mapper.Condition;
import com.renjia.dao.CoreEmployeeCustomerMapper;
import com.renjia.dao.CoreEmployeeMapper;
import com.renjia.dao.CoreUserBankMapper;
import com.renjia.entity.CoreEmployee;
import com.renjia.entity.CoreEmployeeCustomer;
import com.renjia.entity.CoreEmployeeImportBatch;
import com.renjia.entity.CoreUserBank;
import com.renjia.service.generic.log.LogService;
import com.renjia.service.generic.middleware.batchprocessor.payload.AbstractBatchPayload;
import com.renjia.service.module.employeeaddition.advisor.EmployeePiecePostAdvisor;
import com.renjia.service.module.employeeaddition.advisor.EmployeePieceStartAdvisor;
import com.renjia.service.module.employeeaddition.advisor.EmployeeSingleStartAdvisor;
import com.renjia.service.module.employeeaddition.advisorimpl.EmployeeAdvisorTypeEnum;
import com.renjia.service.module.employeeaddition.advisorimpl.common.payload.AbstractEmployeeBatchPayload;
import com.renjia.service.module.employeeaddition.advisorimpl.common.payload.AbstractEmployeePiecePayload;
import com.renjia.service.module.employeeaddition.advisorimpl.common.payload.AbstractEmployeeSinglePayload;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;

/**
 * 人员导入性能优化：人员信息预加载（分片）
 *
 * @author zhang xiao bin
 * @version v1.0
 * @date 2021/03/20
 * @since v5.0.6
 * @since v5.2.6 与自动补全的逻辑发生耦合，如果取消此预加载的逻辑，自动补全的逻辑也需要调整
 **/
@Service
public class EmployeeAdditionPreloadAdvisorImpl<BE, SE, BP extends AbstractEmployeeBatchPayload<BE, SE>, PP extends AbstractEmployeePiecePayload<SE, SP>, SP extends AbstractEmployeeSinglePayload<SE>>
        implements EmployeePieceStartAdvisor<BE, SE, BP, PP, SP>, EmployeeSingleStartAdvisor<BE, SE, BP, PP, SP>, EmployeePiecePostAdvisor<BE, SE, BP, PP, SP> {

    private final CoreEmployeeMapper coreEmployeeMapper;
    private final CoreUserBankMapper coreUserBankMapper;
    private final CoreEmployeeCustomerMapper coreEmployeeCustomerMapper;
    private final LogService logService;
    private final ConcurrentMap<AbstractEmployeePiecePayload<SE, SP>, EmployeeAdditionPreloadPayload> preloadPayloadMap;

    public EmployeeAdditionPreloadAdvisorImpl(CoreEmployeeMapper coreEmployeeMapper,
                                              CoreUserBankMapper coreUserBankMapper,
                                              CoreEmployeeCustomerMapper coreEmployeeCustomerMapper,
                                              LogService logService) {
        this.coreEmployeeMapper = coreEmployeeMapper;
        this.coreUserBankMapper = coreUserBankMapper;
        this.coreEmployeeCustomerMapper = coreEmployeeCustomerMapper;
        this.logService = logService;
        this.preloadPayloadMap = new ConcurrentHashMap<>(16);
    }

    @Override
    public boolean shallProcess(@NonNull AbstractBatchPayload<CoreEmployeeImportBatch, EmployeeAdvisorTypeEnum, BE, SE> advisorPayload) {
        // 分片数据预加载，导入才执行
        return advisorPayload.getAdvisorTypeEnum().getCode() > 0;
    }

    @Override
    public int priority() {
        return 42;
    }

    @Override
    public boolean doPrepareOnPieceStart(@NonNull PP piecePayload, @NonNull BP batchPayload) {
        try {
            List<String> documentNumbers = new ArrayList<>(piecePayload.getSinglePayloadList().size());
            List<String> bankCards = new ArrayList<>(piecePayload.getSinglePayloadList().size());
            piecePayload.getSinglePayloadList().forEach(sp -> {
                if (sp.getDocumentNumber() != null) {
                    documentNumbers.add(sp.getDocumentNumber());
                }
                if (sp.getBankCard() != null) {
                    bankCards.add(sp.getBankCard());
                }
            });
            if (!documentNumbers.isEmpty()) {
                List<CoreEmployee> coreEmployees = coreEmployeeMapper.selectList(Condition.<CoreEmployee>wrapper().in("document_number", documentNumbers));
                // some employee exist
                if (!coreEmployees.isEmpty()) {
                    Map<String, List<CoreEmployee>> employeeMap = coreEmployees.stream().collect(Collectors.groupingBy(this::getEmployeeKey));
                    List<String> employeeIds = coreEmployees.stream().map(CoreEmployee::getId).collect(Collectors.toList());
                    Map<String, List<CoreUserBank>> userBankMap;
                    if (bankCards.isEmpty()) {
                        userBankMap = Collections.emptyMap();
                    } else {
                        userBankMap = coreUserBankMapper.selectList(Condition.<CoreUserBank>wrapper()
                                .in("user_id", employeeIds).in("card_no", bankCards))
                                .stream().collect(Collectors.groupingBy(this::getUserBankKey));
                    }
                    Map<String, List<CoreEmployeeCustomer>> employeeCustomerMap = coreEmployeeCustomerMapper.selectList(Condition.<CoreEmployeeCustomer>wrapper()
                            .eq("channel_merchant_id", batchPayload.getCoreCustomerChannelMerchant().getChannelMerchantId())
                            .eq("customer_id", batchPayload.getCoreCustomerChannelMerchant().getCustomerId())
                            .in("employee_id", employeeIds))
                            .stream().collect(Collectors.groupingBy(CoreEmployeeCustomer::getEmployeeId));
                    preloadPayloadMap.put(piecePayload, new EmployeeAdditionPreloadPayload(employeeMap, userBankMap, employeeCustomerMap));
                }
            }
        } catch (Exception e) {
            logService.error(e, "人员导入分片预加载异常");
        }
        return true;
    }

    @Override
    public boolean doPrepareOnSingleStart(@NonNull SP singlePayload, @NonNull PP piecePayload, @NonNull BP batchPayload) {
        EmployeeAdditionPreloadPayload preloadPayload = preloadPayloadMap.get(piecePayload);
        if (preloadPayload == null) {
            return true;
        }
        List<CoreEmployee> coreEmployees = preloadPayload.getEmployeeMap().get(getEmployeeKey(singlePayload));
        if (coreEmployees == null) {
            return true;
        }
        if (coreEmployees.size() == 1) {
            // coreEmployee preloaded
            singlePayload.setCoreEmployee(coreEmployees.get(0));
            List<CoreUserBank> coreUserBanks = preloadPayload.getUserBankMap().get(getUserBankKey(singlePayload));
            if (coreUserBanks != null) {
                if (coreUserBanks.size() == 1) {
                    // userBank preloaded
                    singlePayload.setCoreUserBank(coreUserBanks.get(0));
                } else if (coreUserBanks.size() > 1) {
                    logService.remind("银行卡信息重复：userId=" + singlePayload.getCoreEmployee().getId() + ", cardNo=" + singlePayload.getBankCard());
                }
            }
            List<CoreEmployeeCustomer> coreEmployeeCustomers = preloadPayload.getEmployeeCustomerMap().get(singlePayload.getCoreEmployee().getId());
            if (coreEmployeeCustomers != null) {
                if (coreEmployeeCustomers.size() == 1) {
                    // employeeCustomer preloaded
                    singlePayload.setCoreEmployeeCustomer(coreEmployeeCustomers.get(0));
                } else if (coreEmployeeCustomers.size() > 1) {
                    logService.remind("用户-客户表信息重复，id=" + coreEmployeeCustomers.stream()
                            .map(CoreEmployeeCustomer::getId).map(String::valueOf).collect(Collectors.joining(",")));
                }
            }
        } else if (coreEmployees.size() > 1) {
            // validation will reject
            singlePayload.getErrMsgList().add("数据库中证件信息重复：" + getEmployeeKey(singlePayload));
            logService.remind("证件信息重复：" + getEmployeeKey(singlePayload));
        }
        return true;
    }

    @Override
    public boolean doCleanUpOnPiecePost(@NonNull PP piecePayload, @NonNull BP batchPayload) {
        preloadPayloadMap.remove(piecePayload);
        return true;
    }

    private String getEmployeeKey(CoreEmployee coreEmployee) {
        return coreEmployee.getDocumentType() + "-" + coreEmployee.getDocumentNumber();
    }

    private String getEmployeeKey(SP singlePayload) {
        return (singlePayload.getDocumentTypeEnum() == null ? "null " : singlePayload.getDocumentTypeEnum().getCode()) + "-" + singlePayload.getDocumentNumber();
    }

    private String getUserBankKey(CoreUserBank coreUserBank) {
        return coreUserBank.getUserId() + "-" + coreUserBank.getCardNo();
    }

    private String getUserBankKey(SP singlePayload) {
        return singlePayload.getCoreEmployee().getId() + "-" + singlePayload.getBankCard();
    }
}
